/** String Utilities
  Written in the D programming language 1.0
  * Authors: Wei Li (oldrev@gmail.com)
  * License: BSD
  * Copyright: Copyright (C) 2007 Wei Li. All rights reserved.
  */


module dotmars.base.string;

import dotmars.runtime.stdclib.string;

import dotmars.base.stdtypes;
import dotmars.base.stdexcept;
import dotmars.base.convert;
import dotmars.text.utf;

public import dotmars.base.collection.array;
 
//TODO: Too much opt needed

size_t cstrlen(T) (T s)
{
	static assert(is(T == dchar*) || 
			is(T == wchar*) || is(T == char*));

	//make compiler happy
	static if(is(T == char*))
		return strlen(s);
	//wcslen? C 的 wchar_t 是不确定地....  

	size_t i;
	if (s)
	{
		while (*s++) ++i;
	}
	return i;
}


char* toStringz (string s)
{
	if (s.ptr !is null) s ~= '\0';
	return s.ptr;
}


wchar* toWstringz (wstring s)
{
	if (s.ptr !is null) s ~= '\0';
	return s.ptr;
}

dchar* toDstringz (dstring s)
{
	if (s.ptr !is null) s ~= '\0';
	return s.ptr;
}


string toString (char* s)
{
	return s ? s[0 .. cstrlen(s)].dup : null;
}


wstring toWstring (wchar* s)
{
	return s ? s[0 .. cstrlen(s)].dup : null;
}


dstring toDstring (dchar* s)
{
	return s ? s[0 .. cstrlen(s)].dup : null;
}



dchar at(string str, uint charIndex)
in {
	assert(charIndex >= 0 && charIndex < str.count());
}
body {
	size_t pos = 0;
	uint ci = 0;
	while(pos < str.length)
	{
		dchar d;
		size_t skip = toUtf32(str[pos..$], d);
		if(ci == charIndex)
			return d;
		pos += skip;
		ci++;
	}

	throw new ArgumentException("at: Out of range");
}

size_t count(string str)
{
	//返回字符个数
	return .count(str);
}


string clone(string str)
in {
	assert(str);
}
body {
	return str.dup;
}


string substring(string str, size_t pos, size_t n)
in {
	assert(str !is null);
}
body {
	return str[pos .. pos + n];
}


bool startsWith(string str, string sub)
in {
	assert(str !is null);
	assert(sub !is null);
}
body {
	//utf-8 没有字节序问题
	if(sub.length > str.length)return false;
	return compare(str[0..sub.length], sub[0..length]) == 0;
}


bool endsWith(string str, string sub)
in {
	assert(str !is null);
	assert(sub !is null);
}
body {
	if(sub.length > str.length)return false;
	return compare(str[str.length - sub.length .. $], sub[0..$]) == 0;
}


// A find algorithm for string with supported UTF-8
int indexOf(string str, dchar c)
{ 
	//查找目标是ASCII字符，UTF-8与ASCII兼容
	if (c <= 0x7F)
	{	
		auto p = cast(char*)memchr(str.ptr, c, str.length);
		if (p !is null)
			return cast(int)(p - cast(char*)str);
		else
			return -1;
	}	

	size_t pos = 0;
	int ci = 0;
	while(pos < str.length)
	{
		dchar d;
		size_t skip = toUtf32(str[pos..$], d);
		if(c == d) return ci;
		pos += skip;
		ci++;	
	}

	return -1;
}


//TODO: 优化这个函数
int lastIndexOf(string str, dchar c)
{
	//这个好像比较低效的说, GNU 有个 memrchr 扩展，考虑什么时候用
	foreach_reverse(size_t i, dchar ch; str)
		if(str[i] == c)return cast(int)i;
	return -1;
}


int indexOfAny(string str, dchar[] chars)
in {
	assert(str !is null);
	assert(chars !is null);
}
body {
	foreach(dchar c; chars)
	{
		int i = str.indexOf(c);
		if(i >= 0 ) return i;
	}
	return -1;
}


int lastIndexOfAny(string str, dchar[] chars)
{
	foreach(dchar c; chars)
	{
		int i = str.lastIndexOf(c);
		if(i >= 0) return i;
	}
	return -1;
}


string replace(string str, dchar oldChar, dchar newChar)
{
	string result = new char[str.length];
	result[] = str[];

	//新老字符都是 ASCII
	if(oldChar <= 0x7F && newChar <= 0x7F) {
		foreach(inout char c; result) {
			if(c == oldChar) 
				c = cast(char) newChar;
		}
		return result;
	}

	//TODO: 效率优化
	foreach(dchar c; result) {
		if(c == oldChar) c = newChar;
	}
	return result;
}


//insertAfter
string insert(string str, size_t startIndex, string value)
in {
    assert(startIndex >= 0 && startIndex <= str.length);
}
body {
    if (value.length == 0) return str;
    if (str.length == 0) return value;

    int newlength = str.length + value.length;
    string result = new char[newlength];

    result[0 .. startIndex] = str[0 .. startIndex];
    result[startIndex .. startIndex + value.length] = value[];
    result[startIndex + value.length .. newlength] = str[startIndex .. str.length];
    return result;
}


string remove(string str, size_t startIndex)
in {
	assert(startIndex >= 0 && startIndex <= str.length);
}
body {
	return str[0 .. startIndex].dup;
}


string remove(string str, size_t startIndex, size_t n)
in {
	assert(startIndex >= 0 && startIndex <= str.length);
	assert(n >= 0 && n <= str.length - startIndex);
}
body {
	string result = new char[str.length - n];
	result[0 .. startIndex ] = str[0 .. startIndex];
	result[startIndex .. $] = str[startIndex + n .. $];
	return result;
}


int compare(string s1, string s2)
{
    auto len = s1.length;
    int result;

    if (s2.length < len)
	len = s2.length;
    result = memcmp(s1.ptr, s2.ptr, len);
    if (result == 0)
	result = cast(int)s1.length - cast(int)s2.length;
    return result;
}


/*
char[] trim(char[] str, dchar[] chars)
{
	size_t startIndex = 0;

}
*/

//TODO: trim trimStart trimEnd
//


string toLower(string str)
{
	string result = new char[str.length];
	for(size_t i = 0; i < str.length; ++i)
	{
		char c = str[i];
		if(c >= 'a' && c <= 'z')
			c = c - 'a' + 'A';
		result[i] = c;
	}
	return result;
}


string toUpper(string str)
{
	string result = new char[str.length];
	for(size_t i = 0; i < str.length; ++i)
	{
		char c = str[i];
		if(c >= 'A' && c <= 'Z')
			c = c - 'A' + 'a';
		result[i] = c;
	}
	return result;
}


string format(string fmt, ...)
in {
	assert(fmt !is null);
}
body {
	string ret;
	Sink!(char) sink = (char c){ ret ~= c; } ;
	Formatter.format(sink, fmt, _argptr, _arguments);
	return ret;
}

